package cn.com.cym.gamezao.interceptor;

import cn.com.cym.gamezao.annotation.AccessLimit;
import cn.com.cym.gamezao.annotation.AuthToken;
import cn.com.cym.gamezao.base.ResultResponse;
import cn.com.cym.gamezao.cache.AccessLimitCache;
import cn.com.cym.gamezao.entity.authorize.Authorize;
import cn.com.cym.gamezao.entity.authorize.RoleAuthorize;
import cn.com.cym.gamezao.entity.authorize.User;
import cn.com.cym.gamezao.entity.authorize.UserRole;
import cn.com.cym.gamezao.exception.BaseException;
import cn.com.cym.gamezao.jwt.JwtUtils;
import cn.com.cym.gamezao.jwt.RequestUser;
import cn.com.cym.gamezao.service.authorize.UserService;
import cn.com.cym.gamezao.util.CommonUtils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Stream;

/**
 * 用户登录拦截器 并把当前登入用户存在线程里面
 */

@Component
public class AuthInterceptor implements HandlerInterceptor {

    private static final HashMap<String,AccessLimitCache> ipCount=new HashMap<>();



    @Resource
    public UserService userService;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){

        String token = request.getHeader("x_token");
        System.out.println("token:"+token);
        if(CommonUtils.isNotEmpty(token) && !JwtUtils.expire(token)){
            String userIdStr = JWT.decode(token).getAudience().get(0);
            System.out.println("userName:"+userIdStr);
            final User user = userService.fetchOneByUserName(userIdStr);
            if(user!=null){
                RequestUser.add(user);
            }
        }
        //如果映射的不是方法 直接返回
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //获取方法名称
        final String methodName = method.getName();

        // 如果为下载数据 需要权限控制 后面再做吧
        /*if(methodName.startsWith("exportData") && CommonUtils.isEmpty(token)){
            throw new BaseException("账号未登录",new ResultResponse("账号未登录", HttpStatus.FORBIDDEN.value()));
        }*/
        //获取方法对应的类
        final String className = method.getDeclaringClass().getName();

        AccessLimit accessLimit = null ;  // handlerMethod.getMethodAnnotation(AccessLimit.class);

        String ip = CommonUtils.customIp(request);
        if(accessLimit!=null){
            long seconds = accessLimit.dateRange();
            int maxCount = accessLimit.limitCount();

            if(!ipCount.containsKey(ip+className+methodName)){
                ipCount.put(ip+className+methodName,new AccessLimitCache(ip,className,methodName));
            }
            AccessLimitCache limitAccess = ipCount.get(ip+className+methodName);

            Integer count = limitAccess.getCount();

            if(count == null || count==0){
                //第一次访问
                limitAccess.setCount(1);
                limitAccess.setFirstAccessTime(new Date());
                limitAccess.setLastAccessTime(limitAccess.getFirstAccessTime());
                ipCount.put(ip+className+methodName,limitAccess);
            }else if(count < maxCount){
                //加1
                limitAccess.setCount(++count);
                limitAccess.setLastAccessTime(new Date());
                ipCount.put(ip+className+methodName,limitAccess);
            }else if(CommonUtils.dateDiff(limitAccess.getLastAccessTime(),new Date())>seconds){
                limitAccess.setCount(1);
                limitAccess.setFirstAccessTime(new Date());
                limitAccess.setLastAccessTime(limitAccess.getFirstAccessTime());
                ipCount.put(ip+className+methodName,limitAccess);
            }else{
                System.out.println(ip+"当前访问次数"+count+",限制访问次数："+maxCount);
                //超出访问次数
                throw  new BaseException("超出访问次数,请"+seconds/60/1000+"分钟后再试",new ResultResponse("超出访问次数,请"+seconds/60/1000+"分钟后再试", 438));
            }
        }
        // 检查有没有需要用户权限的注解  测试开放
        if (false && method.isAnnotationPresent(AuthToken.class)) {
            //获取方法上的注解
            AuthToken userLoginToken = method.getAnnotation(AuthToken.class);

            //获取类上面的注解
            AuthToken annotation = method.getDeclaringClass().getAnnotation(AuthToken.class);

            if (userLoginToken!=null && userLoginToken.require() && (annotation == null || annotation.require())) {
                // 私钥
                String privateKey= request.getHeader("x_key");
                if (token == null) {
                    //throw new NotLoginException("用户未登录，请登录系统");
                    throw new BaseException("账号未登录",new ResultResponse("账号未登录", HttpStatus.FORBIDDEN.value()));
                }
                final Boolean verify = JwtUtils.verify(token);
                if(!verify){
                    throw new  BaseException("验证失败,请重新登录",new ResultResponse("验证失败,请重新登录", HttpStatus.FORBIDDEN.value()));
                }
                // 获取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new BaseException("口令验证失败",new ResultResponse("口令验证失败", HttpStatus.FORBIDDEN.value()));
                }
                User user;
                try{
                    user = userService.fetchOneByUserName(userId);

                    if(!"".equals(userLoginToken.rule())){
                        String rules=(annotation == null || CommonUtils.isEmpty(annotation.rule()))?userLoginToken.rule():annotation.rule()+"."+userLoginToken.rule();

                        System.out.println("rules:"+rules);
                        Iterator<UserRole> iterator = user.getUserRoles().iterator();
                        if(iterator.hasNext()){
                            final UserRole next = iterator.next();
                            final Iterator<RoleAuthorize> authorizeIterator = next.getRole().getRoleAuthorizes().iterator();
                            if(authorizeIterator.hasNext()){
                                Authorize authorize = authorizeIterator.next().getAuthorize();
                                if(authorize.getCode().equals(rules)){
                                    RequestUser.add(user);
                                    //访问成功，将新的token放到响应中
                                    /*final String newToken = JwtUtils.create(user);
                                    response.setHeader("x_token",newToken);*/
                                    return true;
                                }
                            }
                        }
                        // return true;
                        //todo 当前不做角色判断 只看是否登录
                       // throw new BaseException("没有该功能权限",new ResultResponse("没有该功能权限",HttpStatus.FORBIDDEN.value()));
                    }else{
                        RequestUser.add(user);
                        //访问成功，将新的token放到响应中
                        final String newToken = JwtUtils.create(user);
                        response.setHeader("x_token",newToken);
                    }
                } catch (NullPointerException e){
                    throw new BaseException("用户不存在",new ResultResponse("用户不存在", HttpStatus.FORBIDDEN.value()));
                }
                // 验证 token
                /*JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                    user.setCurrentIp(ip);
                    RequestUser.add(user);
                } catch (JWTVerificationException e) {
                    //throw new NotLoginException("token验证失败,请重新登录");
                    throw new BaseException("验证失败,请重新登录",new ResultResponse("验证失败,请重新登录", HttpStatus.FORBIDDEN.value()));
                }*/
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        RequestUser.remove();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
